package com.device.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.device.dataobject.ro.*;
import com.device.dataobject.vo.ResultVO;
import com.device.entity.*;
import com.device.enums.CacheEnum;
import com.device.enums.FileType;
import com.device.enums.ResultEnum;
import com.device.repository.*;
import com.device.util.AESUtil;
import com.device.util.JWTUtil;
import com.device.util.ResultVOUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service

public class UserService {
    private final UserRepository userRepository;

    private final RentRepository rentRepository;

    private final RegisterRepository registerRepository;

    private final WxService wxService;

    private final PostRepository postRepository;

    private final FileUploadService fileUploadService;

    private final UserDeviceRepository userDeviceRepository;
    private final RedisTemplate<String,Object> redisTemplate;
    @Autowired
    public UserService(UserRepository userRepository, RentRepository rentRepository, RegisterRepository registerRepository, WxService wxService, PostRepository postRepository, FileUploadService fileUploadService, UserDeviceRepository userDeviceRepository, RedisTemplate<String,Object> redisTemplate) {
        this.userRepository = userRepository;
        this.rentRepository = rentRepository;
        this.registerRepository = registerRepository;
        this.wxService = wxService;
        this.postRepository = postRepository;
        this.fileUploadService = fileUploadService;
        this.userDeviceRepository = userDeviceRepository;
        this.redisTemplate = redisTemplate;
    }


    public ResultVO findById(Integer id) {

        return ResultVOUtil.returnSuccess(userRepository.findById(id).orElse(null));
    }

    public ResultVO uploadAvatar(Integer userId, MultipartFile file) {
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            return ResultVOUtil.returnFail(ResultEnum.ERROR_ID);
        }

        // Upload image to OSS
        AliyunOssResult result = fileUploadService.uploadFile(file, FileType.AVATAR);

        // Update avatar URL and save to the database
        user.setAvatar(result.getUrl());
        userRepository.save(user);

        return ResultVOUtil.returnSuccess(user);
    }

    public ResultVO uploadPoster(Integer userId, MultipartFile file) {
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            return ResultVOUtil.returnFail(ResultEnum.ERROR_ID);
        }

        // Upload image to OSS
        AliyunOssResult result = fileUploadService.uploadFile(file, FileType.POSTER);

        // Update avatar URL and save to the database
        user.setPoster(result.getUrl());
        userRepository.save(user);

        return ResultVOUtil.returnSuccess(user);
    }


    public ResultVO create(WxUserInfoRO userRO) {
        // 根据微信传来的code换取微信用户的openid
        JSONObject jsonObject = wxService.getWxUserInfo(userRO.getCode());
        String sessionKey = jsonObject.getString("session_key");
        String openid = jsonObject.getString("openid");

        jsonObject = WxService.getUserInfo(userRO.getEncryptedData(), sessionKey, userRO.getIv());

        // 根据openid查询用户是否存在
        User user = userRepository.findByOpenid(openid);
        boolean isNewUser = false;

        if (user == null) {
            // 这是新用户
            user = new User();
            user.setOpenid(openid);
            isNewUser = true;

            if (jsonObject != null) {
                user.setNickName(userRO.getNickName());
                user.setAvatar(jsonObject.getString("avatarUrl"));
                user.setGender(jsonObject.getInteger("gender"));
            }
        }

        // 生成token
        jsonObject = new JSONObject();
        jsonObject.put("id", user.getId());

        try {
            String token = JWTUtil.createJWT(JSON.toJSONString(jsonObject));
            user.setToken(token);
            userRepository.save(user);

            // 更新userCount
            if (isNewUser) {
                updateAndCacheUserCount();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ResultVOUtil.returnSuccess(user);
    }

    private void updateAndCacheUserCount() {


        // 尝试从Redis缓存中获取当前用户数量
        Long cachedCount = (Long) redisTemplate.opsForValue().get(CacheEnum.USER_COUNT.name());

        if (cachedCount != null) {
            // 更新用户数量
            Long updatedCount = cachedCount + 1;

            // 将更新后的用户数量存入Redis缓存中
            redisTemplate.opsForValue().set(CacheEnum.USER_COUNT.name(), updatedCount);
        }
    }



    public ResultVO update(Integer userId, UpdateDef[] opArray) {
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            return ResultVOUtil.returnFail(ResultEnum.ERROR_ID);
        }

        for (UpdateDef def : opArray) {
            String value = def.getValue();
            switch (def.getPath()) {
                case "/nick_name":
                    user.setNickName(value);
                    break;
                case "/gender":
                    user.setGender(Integer.valueOf(value));
                    break;
                case "/password":
                    user.setPassword(AESUtil.encrypt(value));
                    break;
            }
        }

        if (opArray.length > 0) {
            userRepository.save(user);
        }

        return ResultVOUtil.returnSuccess(user);
    }

    public ResultVO getUserRent(Integer userId, Integer page, Integer size) {
        User user = getUserById(userId);
        if (user != null && user.getIsRegister()) {
            Pageable pageable = PageRequest.of(page - 1, size);
            Page<Rent> data = rentRepository.findAllByUserId(userId, pageable);
            return ResultVOUtil.returnSuccess(data.getContent());
        } else {
            return ResultVOUtil.returnFail(401, "no rights");
        }
    }

    public ResultVO getRentDetail(Integer userId, Integer id) {
        User user = getUserById(userId);
        Rent rent = rentRepository.findById(id).orElse(null);
        if (rent != null && (userId.equals(rent.getUser().getId()) || isAdmin(user))) {
            return ResultVOUtil.returnSuccess(rent);
        } else {
            return ResultVOUtil.returnFail();
        }
    }

    public ResultVO deleteRent(Integer userId, Integer id) {
        User user = getUserById(userId);
        Rent rent = rentRepository.findById(id).orElse(null);
        if (rent != null && (userId.equals(rent.getUser().getId()) || isAdmin(user))&&(rent.getStatus().equals("拒绝")||rent.getStatus().equals("租赁已到期"))) {
            rentRepository.deleteById(id);
            return ResultVOUtil.returnSuccess();
        } else {
            return ResultVOUtil.returnFail();
        }
    }

    public ResultVO getUserPost(Integer userId, Integer page, Integer size) {
        User user = getUserById(userId);
        if (user != null && user.getIsRegister()) {
            Pageable pageable = PageRequest.of(page - 1, size);
            Page<Post> data = postRepository.findAllByUserId(userId, pageable);
            return ResultVOUtil.returnSuccess(data.getContent());
        } else {
            return ResultVOUtil.returnFail(401, "no rights");
        }
    }

    public ResultVO getPostDetail(Integer userId, Integer id) {
        User user = getUserById(userId);
        Post post = postRepository.findById(id).orElse(null);
        if (post != null && (userId.equals(post.getUser().getId()) || isAdmin(user))) {
            return ResultVOUtil.returnSuccess(post);
        } else {
            return ResultVOUtil.returnFail();
        }
    }

    public ResultVO deletePost(Integer userId, Integer id) {
        User user = getUserById(userId);
        Post post = postRepository.findById(id).orElse(null);
        if (post != null && (userId.equals(post.getUser().getId()) || isAdmin(user)) && post.getStatus().equals("拒绝")) {
            postRepository.deleteById(id);
            return ResultVOUtil.returnSuccess();
        } else {
            return ResultVOUtil.returnFail();
        }
    }

    public ResultVO post(Integer userId, PostRO postRO) {
        User user = getUserById(userId);
        Integer orderId = postRO.getOrderId();
        if (user != null && user.getIsRegister() && orderId == null) {
            Post post = createOrUpdatePost(postRO, user);
            postRepository.save(post);
            return ResultVOUtil.returnSuccess();
        } else if (user != null && user.getIsRegister()) {
            Post existingPost = postRepository.findById(orderId).orElse(null);
            if (existingPost != null && (userId.equals(existingPost.getUser().getId()) || isAdmin(user))) {
                Post updatedPost = createOrUpdatePost(postRO, user);
                updatedPost.setId(orderId);
                postRepository.save(updatedPost);
            }
            return ResultVOUtil.returnSuccess();
        } else {
            return ResultVOUtil.returnFail(401, "no rights");
        }
    }

    public ResultVO rent(Integer userId, RentRO rentRO) {
        User user = getUserById(userId);
        Integer orderId = rentRO.getOrderId();
        long timeDifference = rentRO.getEndTime().getTime() - rentRO.getCreateTime().getTime();
        long days = TimeUnit.MILLISECONDS.toDays(timeDifference);
        if (user != null && user.getIsRegister() && orderId == null &&days>=1) {
            List<UserDevice> userDevices = userDeviceRepository.findByDeviceNameAndAreaAndInstituteNameAndStatus(rentRO.getDeviceName(), rentRO.getDevice_area(), rentRO.getInstitute_name(), true);
            if (rentRO.getDevice_num() > userDevices.size() || rentRO.getDevice_num() <= 0) {
                return ResultVOUtil.returnFail(402, "设备数量有误");
            }
            Rent rent = createOrUpdateRent(rentRO, user);
            rentRepository.save(rent);
            return ResultVOUtil.returnSuccess();
        } else if (user != null && user.getIsRegister() &&days>=1) {
            Rent existingRent = rentRepository.findById(orderId).orElse(null);
            if (existingRent != null && (userId.equals(existingRent.getUser().getId()) || isAdmin(user))) {
                List<UserDevice> userDevices = userDeviceRepository.findByDeviceNameAndAreaAndInstituteNameAndStatus(rentRO.getDeviceName(), rentRO.getDevice_area(), rentRO.getInstitute_name(), true);
                if (rentRO.getDevice_num() > userDevices.size() || rentRO.getDevice_num() <= 0) {
                    return ResultVOUtil.returnFail(402, "设备数量有误");
                }
                Rent updatedRent = createOrUpdateRent(rentRO, user);
                updatedRent.setId(orderId);
                rentRepository.save(updatedRent);
            }
            return ResultVOUtil.returnSuccess();
        } else if (days < 1 ){
            return ResultVOUtil.returnFail(402,"租赁时长不足一天");
        }
        else {
            return ResultVOUtil.returnFail(401, "no rights");
        }
    }

    public ResultVO login(LoginRO loginRO) {
        String username = loginRO.getUsername();
        String password = loginRO.getPassword();

        User user = userRepository.findByUserName(username);

        if (user != null) {
            if (password.equals(AESUtil.decrypt(user.getPassword())) && user.getIsRegister()) {
                return ResultVOUtil.returnSuccess(user.getToken());
            }
            return ResultVOUtil.returnFail(404, "正在审批中");
        }

        return ResultVOUtil.returnFail(404, "请前往小程序进行注册");
    }

    public ResultVO register(Integer userId, RegisterRO registerRO) {
        String username = registerRO.getUsername();
        String password = registerRO.getPassword();

        User user = getUserById(userId);
        if (user != null) {
            Register register = createRegister(registerRO, user);
            registerRepository.save(register);
            user.setUserName(username);
            user.setPassword(AESUtil.encrypt(password));
            userRepository.save(user);
            return ResultVOUtil.returnSuccess();
        }

        return ResultVOUtil.returnFail();
    }

    private User getUserById(Integer userId) {
        return userRepository.findById(userId).orElse(null);
    }

    private boolean isAdmin(User user) {
        return user != null && user.getIsAdmin();
    }

    private Post createOrUpdatePost(PostRO postRO, User user) {
        Post post = new Post();
        post.setDeviceName(postRO.getDeviceName());
        post.setInstituteName(postRO.getInstituteName());
        post.setDevice_area(postRO.getDevice_area());
        post.setDevice_num(postRO.getDevice_num());
        post.setRentPrice(postRO.getRentPrice());
        post.setPhoneNum(postRO.getPhoneNum());
        post.setUser(user);
        return post;
    }

    private Rent createOrUpdateRent(RentRO rentRO, User user) {
        Rent rent = new Rent();
        rent.setDevice_area(rentRO.getDevice_area());
        rent.setDevice_num(rentRO.getDevice_num());
        rent.setDeviceName(rentRO.getDeviceName());
        rent.setRentInstitute(rentRO.getRentInstitute());
        rent.setInstitute_name(rentRO.getInstitute_name());
        rent.setPhoneNum(rentRO.getPhoneNum());
        rent.setCreateTime(rentRO.getCreateTime());
        rent.setEndTime(rentRO.getEndTime());

        long timeDifference = rentRO.getEndTime().getTime() - rentRO.getCreateTime().getTime();
        long days = TimeUnit.MILLISECONDS.toDays(timeDifference);
        rent.setTotalPrice(calculateTotalPrice(rentRO, days));

        rent.setUser(user);
        return rent;
    }

    private double calculateTotalPrice(RentRO rentRO, long days) {
        UserDevice userDevice = userDeviceRepository.findByDeviceNameAndAreaAndInstituteNameAndStatus(rentRO.getDeviceName(), rentRO.getDevice_area(), rentRO.getInstitute_name(), true).get(0);
        Integer rentPrice = userDevice.getRentPrice();
        int deviceNum = rentRO.getDevice_num();
        return rentPrice * deviceNum * days;
    }


    private Register createRegister(RegisterRO registerRO, User user) {
        Register register = new Register();
        register.setAppendix(registerRO.getAppendix());
        register.setName(registerRO.getNickName());
        register.setInstituteName(registerRO.getInstituteName());
        register.setPhoneNumber(registerRO.getPhoneNumber());
        register.setPosition(registerRO.getPosition());
        register.setUser(user);
        return register;
    }


}
